#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>
#include "Socket.hpp"
#include "Log.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include "Common.hpp"

class Connection;
class TcpServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>;
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_out = (EPOLLOUT | EPOLLET);

class Connection
{
public:
    Connection(int sock, std::shared_ptr<TcpServer> tcp_server_ptr) : m_sock(sock), m_tcp_server_ptr(tcp_server_ptr)
    {
    }

    void SetHandler(func_t recv_cb, func_t send_cb, func_t excute_cb)
    {
        m_recv_cb = recv_cb;
        m_send_cb = send_cb;
        m_excute_cb = excute_cb;
    }

    int SockFd()
    {
        return m_sock;
    }

    ~Connection() {}

private:
    int m_sock;
    std::string m_inbuffer; // string没办法处理二进制流
    std::string m_outbuffer;

public:
    func_t m_recv_cb;
    func_t m_send_cb;
    func_t m_excute_cb;

    // 添加一个回指指针
    std::shared_ptr<TcpServer> m_tcp_server_ptr;
};

class TcpServer : public NoCopy
{
    static const int num = 64;

public:
    TcpServer(uint16_t port) : m_port(port), m_epoller_ptr(new Epoller()), m_listenSock_ptr(new Sock()), m_quit(true)
    {
        // log(INFO, "init success ...");
    }

    void Init()
    {
        m_listenSock_ptr->Socket();
        m_listenSock_ptr->SetNoBlock();
        m_listenSock_ptr->Bind(m_port);
        m_listenSock_ptr->Listen();
        AddConnection(m_listenSock_ptr->Fd(), EVENT_IN, \
        std::bind(&TcpServer::Accepter, this, std::placeholders::_1),
        nullptr, nullptr);
    }

    void AddConnection(int fd, uint32_t event, func_t recv_cb, func_t send_cb, func_t excute_cb)
    {
        // 1、给也建立fd一个connection对象，将listensock和connection放入m_connections中。
        std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(fd, (std::shared_ptr<TcpServer>)this);
        new_connection->SetHandler(recv_cb, send_cb, excute_cb);

        // 2、添加到unordered_map
        m_connections.insert(std::make_pair(fd, new_connection));

        // 3、我们添加对应的事件，除了要加到内核中，fd,event
        m_epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, fd, event);
    }

    void Accepter(std::shared_ptr<Connection> connection)
    {
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->SockFd(), (struct sockaddr*)&peer, &len);
            if(sock > 0)
            {
                SetNoBlock(sock);
                AddConnection(sock, EVENT_IN, nullptr, nullptr, nullptr);
            }
            else
            {
                if(errno == EWOULDBLOCK) break;
                else if(errno == EINTR) continue;
                else break;
            }   
        }
    }

    bool IsConnectionSafe(int fd)
    {
        auto iter = m_connections.find(fd);
        if(iter == m_connections.end())
            return false;
        else   
            return true;
    }

    void Dispatcher(int timeout)
    {
        int n = m_epoller_ptr->EpollerWait(m_revs, num, timeout);
        for (int i = 0; i < num; ++i)
        {
            uint32_t events = m_revs[i].events;
            int sock = m_revs[i].data.fd;
            // 统一把事件异常转化为读写问题
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);
            
            // 只需要处理读写
            if ((events & EVENT_IN) && IsConnectionSafe(sock))
            {
                if(m_connections[sock]->m_recv_cb)
                    m_connections[sock]->m_recv_cb(m_connections[sock]);
            }
            if ((events & EVENT_out) && IsConnectionSafe(sock))
            {
                if(m_connections[sock]->m_send_cb)
                    m_connections[sock]->m_send_cb(m_connections[sock]);
            }
        }
    }

    void Loop()
    {
        m_quit = false;
        // 我们添加对应的事件，除了要加到内核中，
        // 还要给listen也建立一个connection对象，将listensock和connection放入m_connections中。
        // m_epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, m_listenSock_ptr->Fd(), EVENT_IN);
        // AddConnection();
        while (!m_quit)
        {
            Dispatcher(3000);
            PrintConnection();
        }
        m_quit = true;
    }

    void PrintConnection()
    {
        std::cout << "m_connections fd list: ";
        for(auto& connection : m_connections)
        {
            std::cout << connection.second->SockFd() << ",";
        }
        std::cout << std::endl;
    }
    ~TcpServer()
    {
    }

private:
    std::shared_ptr<Epoller> m_epoller_ptr;
    std::shared_ptr<Sock> m_listenSock_ptr;
    std::unordered_map<int, std::shared_ptr<Connection>> m_connections;
    struct epoll_event m_revs[num];
    uint16_t m_port;
    bool m_quit;
};